import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'interceptors/header_interceptor.dart';
import 'interceptors/log_interceptor.dart';
import 'resp_data.dart' as resp;

/// 实现：
/// 支持common header、可设置的base url，header的拦截器在每次请求时进行
/// 支持get post
/// 支持添加header拦截器、内置打印拦截器，拦截每个请求是否log
/// 连接超时设置、接收超时设置、超时code
/// 耗时统计(interceptor?)

final dio = DioManager();

typedef GetHeader = Map<String, dynamic> Function();
typedef FromJson = dynamic Function(dynamic json);

const int dioError = -1008;

enum Method {
  get,
  post,
}

class DioManager {
  factory DioManager() => _instance;
  static final DioManager _instance = DioManager._internal();

  DioManager._internal();

  final _dio = Dio();

  String _defaultBaseUrl = "www.default.com";
  bool _isInit = false;
  bool _defaultLogPrint = true;

  GetHeader? get getHeader => _getHeader;
  GetHeader? _getHeader;

  bool get logPrint => _logPrint;
  bool _logPrint = false;

  void init(
    String defaultBaseUrl, {
    int connectTimeout = 8000,
    int receiveTimeout = 8000,
    GetHeader? commonHeader,
    bool defaultLogPrint = true,
  }) {
    assert(defaultBaseUrl.isNotEmpty, "initDio: defaultBaseUrl can not be empty");
    if (_isInit) {
      debugPrint("already init dio!!");
      return;
    }
    _defaultLogPrint = defaultLogPrint;
    _getHeader = commonHeader;
    _defaultBaseUrl = defaultBaseUrl;
    // milliseconds
    _dio.options.connectTimeout = connectTimeout;
    _dio.options.receiveTimeout = receiveTimeout;
    // pay attention to interceptors' adding order, log should be last or log is incomplete.
    _dio.interceptors.add(HeaderInterceptor());
    _dio.interceptors.add(LogsInterceptor());
    _isInit = true;
  }

  // only support [get, post] for now
  Future<resp.RespData> network(
    Method method,
    String path, {
    bool returnEntity = true,
    String? baseUrl,
    Map<String, dynamic>? params,
    ResponseType responseType = ResponseType.json,
    Map<String, dynamic>? headers,
    bool? logPrint,
    bool? isEntity = true,
    FromJson? fromJson,
  }) async {
    _logPrint = logPrint ?? _defaultLogPrint;
    _dio.options.baseUrl = (baseUrl ?? _defaultBaseUrl);
    Response? dioRes;
    try {
      switch (method) {
        case Method.get:
          dioRes = await _dio.get(
            path,
            queryParameters: params,
            options: Options(
              responseType: responseType,
              headers: headers,
            ),
          );
          break;
        case Method.post:
          dioRes = await _dio.post(
            path,
            queryParameters: params,
            options: Options(
              responseType: responseType,
              headers: headers,
            ),
          );
          break;
      }
      _logPrint = _defaultLogPrint;
      return resp.RespData(
        code: 0,
        statusCode: dioRes.statusCode,
        data: null != fromJson ? fromJson.call(dioRes.data) : dioRes.data,
      );
    } on DioError catch (e) {
      debugPrint("[dio error]: ${e.message}, method: ${e.requestOptions.method}, request: "
          "${_dio.options.baseUrl}$path, queryParameters: "
          "${e.requestOptions.queryParameters}, headers: ${e.requestOptions.headers}");
      _logPrint = _defaultLogPrint;
      return resp.RespData(message: e.message, code: dioError, dioErrorType: e.type);
    }
  }

  void addInterceptor(Interceptor interceptor) {
    _dio.interceptors.add(interceptor);
  }
}
